package cn.lingyangwl.agile.food.core.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.lingyangwl.agile.food.model.rqrs.store.*;
import cn.lingyangwl.agile.model.constants.CommonCons;
import cn.lingyangwl.agile.model.constants.TenantCons;
import cn.lingyangwl.agile.model.module.auth.AuthUserTenant;
import cn.lingyangwl.agile.food.core.assembly.StoreAssembly;
import cn.lingyangwl.agile.food.core.manager.StoreManager;
import cn.lingyangwl.agile.food.core.mapper.StoreMapper;
import cn.lingyangwl.agile.food.model.entity.ShopStore;
import cn.lingyangwl.agile.food.core.service.StoreService;
import cn.lingyangwl.agile.food.core.utils.DistanceUtil;
import cn.lingyangwl.agile.system.SystemApi;
import cn.lingyangwl.agile.system.model.rqrs.tenant.TenantSaveReq;
import cn.lingyangwl.agile.system.model.rqrs.tenant.TenantUpdateReq;
import cn.lingyangwl.framework.mybatis.plus.utils.PageUtil;
import cn.lingyangwl.framework.tool.core.CollectionUtils;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.gavaghan.geodesy.Ellipsoid;
import org.gavaghan.geodesy.GlobalCoordinates;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 门店
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class StoreServiceImpl extends ServiceImpl<StoreMapper, ShopStore>
    implements IService<ShopStore>, StoreService {

    @Resource
    private StoreManager storeManager;

    @Resource
    private StoreAssembly storeAssembly;
    @Resource
    private SystemApi systemApi;
    
    @Override
    public IPage<StoreResp> pageStore(StorePageReq req) {
        LocalDateTime beginTime = req.getBeginLocalDateTime();
        LocalDateTime endTime = req.getEndLocalDateTime();
        LambdaQueryWrapper<ShopStore> lqw = ShopStore.lqw()
            .eq(StringUtils.isNotEmpty(req.getStatus()), ShopStore::getStatus, req.getStatus())
            .ge(Objects.nonNull(beginTime), ShopStore::getCreatedAt, beginTime)
            .le(Objects.nonNull(endTime), ShopStore::getCreatedAt, endTime)
            .orderByDesc(ShopStore::getCreatedAt);
        IPage<ShopStore> pageData = this.page(PageUtil.to(req), lqw);
        return PageUtil.to(pageData, storeAssembly::toStoreResp);
    }
    
    @Override
    public List<StoreResp> listStore(StoreListReq req) {
        LambdaQueryWrapper<ShopStore> lqw = ShopStore.lqw().eq(ShopStore::getStatus, CommonCons.UseStats.NORMAL)
                .in(CollUtil.isNotEmpty(req.getTenantIds()), ShopStore::getId, req.getTenantIds())
                .like(StringUtils.isNotEmpty(req.getName()), ShopStore::getName, req.getName());

        List<ShopStore> list = this.list(lqw);


        if (ObjectUtils.allNotNull(req.getLatitude(), req.getLongitude())) {
            // 用户当前坐标
            GlobalCoordinates userSource = new GlobalCoordinates(req.getLatitude(), req.getLongitude());
            return list.stream()
                    .map(e -> {
                        StoreResp resp = storeAssembly.toStoreResp(e);
                        ShopStore.AddressDetail storeAddress = Optional.ofNullable(resp.getAddressDetail()).orElse(new ShopStore.AddressDetail());
                        ShopStore.AddressLocation storeLocation = storeAddress.getLocation();
                        if (Objects.isNull(storeLocation)) {
                            resp.setDistance(1000);
                            return resp;
                        }

                        // 店铺位置坐标
                        GlobalCoordinates businessTarget = new GlobalCoordinates(storeLocation.getLat(), storeLocation.getLng());
                        double distanceDouble = DistanceUtil.getDistanceMeter(userSource, businessTarget, Ellipsoid.Sphere);
                        int distance = (int) distanceDouble;
                        resp.setDistance(distance);
                        resp.setDistanceUnit("m");
                        if (distance > 1000) {
                            resp.setDistance(distance / 1000);
                            resp.setDistanceUnit("km");
                        }
                        return resp;
                    }).sorted(Comparator.comparing(StoreResp::getDistance)).collect(Collectors.toList());
        } else {
            return list.stream().map(storeAssembly::toStoreResp).collect(Collectors.toList());
        }
    }

    @Override
    public StoreResp getStore(String id) {
        ShopStore entity = this.getById(id);
    	return storeAssembly.toStoreResp(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveStore(StoreSaveReq req) {
        ShopStore entity = storeAssembly.toStore(req);

        TenantSaveReq tenantSaveReq = new TenantSaveReq();
        tenantSaveReq.setLogo(entity.getLogo()).setName(entity.getName()).setStatus(TenantCons.Status.NORMAL);
        String tenantId = systemApi.saveTenant(tenantSaveReq);
        entity.setId(tenantId);
        return this.save(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateStore(StoreUpdateReq req) {
        ShopStore entity = storeAssembly.toStore(req);
        ShopStore dbStore = this.getById(entity.getId());
        if (Objects.isNull(dbStore)) {
            return true;
        }
        boolean ret = this.updateById(entity);

        TenantUpdateReq updateReq = new TenantUpdateReq();
        updateReq.setId(dbStore.getId()).setName(req.getName()).setLogo(req.getLogo())
                        .setStatus(req.getStatus());
        systemApi.updateTenant(updateReq);
        return ret;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteStore(Long id) {
        ShopStore shopStore = this.getById(id);
        if (Objects.isNull(shopStore)) {
            return true;
        }
        if (AuthUserTenant.mainTenant(shopStore.getMainTenantId(), shopStore.getId())) {
            throw new BizException("不能操作总店");
        }
        boolean ret = this.removeById(id);
        systemApi.deleteTenant(shopStore.getId());
        return ret;
    }
    
    @Override
    public boolean batchDeleteStore(List<Long> ids) {
        List<ShopStore> stores = this.listByIds(ids);
        if (CollectionUtils.isEmpty(stores)) {
            return true;
        }
        if (stores.stream().anyMatch(e -> AuthUserTenant.mainTenant(e.getMainTenantId(), e.getId()))) {
            throw new BizException("不能操作总店");
        }
        boolean ret = this.removeByIds(ids);
        stores.forEach(e -> systemApi.deleteTenant(e.getId()));
    	return ret;
    }

    @Override
    public ShopStore getTenantRefStore(String tenantId) {
        return this.getOne(ShopStore.lqw().eq(ShopStore::getId, tenantId));
    }

    @Override
    public void storeSetting(StoreSettingReq req) {
        ShopStore shopStore = storeAssembly.toStore(req);
        this.updateById(shopStore);
    }
}
